package com.lhd.qd.module.system.generator.controller;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableFill;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.lhd.qd.common.base.ApiResult;
import com.lhd.qd.common.base.BaseController;
import com.lhd.qd.common.exception.BusinessException;
import com.lhd.qd.config.QdProperties;
import com.lhd.qd.module.system.generator.model.dto.GeneratorDTO;
import com.lhd.qd.module.system.generator.model.dto.GeneratorTemplateDTO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Api(tags = "代码生成")
@RequestMapping("generator")
@RestController
public class GeneratorController extends BaseController {

    private static final String LOGIC_DELETE_FIELD_NAME = "deleted";

    private static final String AUDIT_CREATE_TIME_COLUMN_NAME = "create_time";
    private static final String AUDIT_CREATE_USER_COLUMN_NAME = "create_user_id";
    private static final String AUDIT_UPDATE_TIME_COLUMN_NAME = "update_time";
    private static final String AUDIT_UPDATE_USER_COLUMN_NAME = "update_user_id";

    private static final String PARENT_PACKAGE = "com.lhd.qd.module";
    private static final String PARENT_DIR = "com/lhd/qd/module";

    private static final String TEMPLATE_ROOT_PATH = "/template/server";

    @Value("${spring.datasource.druid.username}")
    private String username;

    @Value("${spring.datasource.druid.password}")
    private String password;

    @Value("${spring.datasource.druid.url}")
    private String url;

    @Autowired
    private QdProperties qdProperties;

    @ApiOperation(value = "生成服务端整套代码")
    @PostMapping("server/all")
    public ResponseEntity<ApiResult> generateServerAll(@Validated @RequestBody GeneratorDTO dto) {

        checkProfile();

        AutoGenerator mpg = new AutoGenerator();

        mpg.setGlobalConfig(getGlobalConfig(dto.getOutputDir()));

        mpg.setDataSource(getDatasourceConfig(url, username, password));

        mpg.setTemplate(getTemplateConfig());

        mpg.setStrategy(getStrategyConfig(dto.getTableName(), dto.getTablePrefix()));

        // 如果未设置实体的包路径，根据表名和表前缀推算
        String entityPackage = StringUtils.isEmpty(dto.getEntityPackage())
                ? StringUtils.join(dto.getTableName().replace(dto.getTablePrefix(), "").split("_"), ".")
                : dto.getEntityPackage();
        mpg.setCfg(getInjectionConfig(
                dto.getOutputDir(),
                dto.getModuleName(),
                entityPackage,
                dto.getIsLogicDelete()));

        mpg.execute();

        return success();
    }

    @ApiOperation(value = "生成服务端实体")
    @PostMapping("server/entity")
    private ResponseEntity<ApiResult> generateServerEntity() {

        checkProfile();

        return success();
    }

    @ApiOperation(value = "生成服务端Mapper.xml")
    @PostMapping("server/mapper")
    private ResponseEntity<ApiResult> generateServerMapperXml() {

        checkProfile();

        return success();
    }

    @ApiOperation(value = "生成Web端整套代码")
    @PostMapping("web/all")
    private ResponseEntity<ApiResult> generateWebAll() {

        checkProfile();

        return success();
    }

    private static GlobalConfig getGlobalConfig(String outputDir) {

        GlobalConfig globalConfig = new GlobalConfig();
        globalConfig.setOutputDir(outputDir);
        globalConfig.setOpen(false);

        return globalConfig;
    }

    private static DataSourceConfig getDatasourceConfig(String url, String username, String password) {

        DataSourceConfig dataSourceConfig = new DataSourceConfig();
        dataSourceConfig.setUrl(url);
        dataSourceConfig.setDriverName("com.mysql.jdbc.Driver");
        dataSourceConfig.setUsername(username);
        dataSourceConfig.setPassword(password);

        return dataSourceConfig;
    }

    private static TemplateConfig getTemplateConfig() {

        TemplateConfig templateConfig = new TemplateConfig();
        // 取消默认配置，使用自定义输出路径
        templateConfig.setController("");
        templateConfig.setService("");
        templateConfig.setServiceImpl("");
        templateConfig.setEntity("");
        templateConfig.setMapper("");
        templateConfig.setXml("");

        return templateConfig;
    }

    private static StrategyConfig getStrategyConfig(String tableName, String tablePrefix) {

        List<TableFill> tableFillList = new ArrayList<TableFill>() {{
            add(new TableFill(AUDIT_CREATE_TIME_COLUMN_NAME, FieldFill.INSERT));
            add(new TableFill(AUDIT_CREATE_USER_COLUMN_NAME, FieldFill.INSERT));
            add(new TableFill(AUDIT_UPDATE_TIME_COLUMN_NAME, FieldFill.UPDATE));
            add(new TableFill(AUDIT_UPDATE_USER_COLUMN_NAME, FieldFill.UPDATE));
        }};

        StrategyConfig strategyConfig = new StrategyConfig();
        strategyConfig.setNaming(NamingStrategy.underline_to_camel);
        strategyConfig.setColumnNaming(NamingStrategy.underline_to_camel);
        strategyConfig.setInclude(tableName);
        strategyConfig.setTablePrefix(tablePrefix);
        strategyConfig.setControllerMappingHyphenStyle(true);
        strategyConfig.setLogicDeleteFieldName(LOGIC_DELETE_FIELD_NAME);
        strategyConfig.setTableFillList(tableFillList);

        return strategyConfig;
    }

    private static InjectionConfig getInjectionConfig(String outputDir, String moduleName, String entityPackage, Boolean isLogicDelete) {

        String entityDir = entityPackage.contains(".") ? entityPackage.replace(".", "/") : entityPackage;

        String javaRootPath = String.format("%s/src/main/java/%s/%s/%s", outputDir, PARENT_DIR, moduleName, entityDir);
        String resourcesRootPath = String.format("%s/src/main/resources/mapper/%s", outputDir, moduleName);
        String basePackage = String.format("%s.%s.%s", PARENT_PACKAGE, moduleName, entityPackage);

        Map<String, Object> objectMap = new HashMap<String, Object>() {{
            put("basePackage", basePackage);
            put("isLogicDelete", isLogicDelete);
            put("auditCreateTimeColumnName", AUDIT_CREATE_TIME_COLUMN_NAME);
            put("auditCreateUserColumnName", AUDIT_CREATE_USER_COLUMN_NAME);
            put("auditUpdateTimeColumnName", AUDIT_UPDATE_TIME_COLUMN_NAME);
            put("auditUpdateUserColumnName", AUDIT_UPDATE_USER_COLUMN_NAME);
        }};

        InjectionConfig injectionConfig = new InjectionConfig() {
            @Override
            public void initMap() {
                // 模板中使用${cfg.key}
                this.setMap(objectMap);
            }
        };
        injectionConfig.setFileOutConfigList(getFileOutConfigList(TEMPLATE_ROOT_PATH, resourcesRootPath, javaRootPath));

        return injectionConfig;
    }

    /**
     * 自定义文件输出位置
     *
     * @param templateRootPath
     * @param resourcesRootPath
     * @param javaRootPath
     * @return
     */
    private static List<FileOutConfig> getFileOutConfigList(String templateRootPath, String resourcesRootPath, String javaRootPath) {

        List<GeneratorTemplateDTO> javaFilePathList = new ArrayList<GeneratorTemplateDTO>() {{

            add(new GeneratorTemplateDTO("controller/controller.java.vm", "controller", "Controller.java"));

            add(new GeneratorTemplateDTO("service/service.java.vm", "service", "Service.java"));
            add(new GeneratorTemplateDTO("service/impl/serviceImpl.java.vm", "service/impl", "ServiceImpl.java"));

            add(new GeneratorTemplateDTO("dao/mapper.java.vm", "dao", "Mapper.java"));

            add(new GeneratorTemplateDTO("model/dataobj/dataObj.java.vm", "model/dataobj", "DO.java"));

            add(new GeneratorTemplateDTO("model/dto/saveDTO.java.vm", "model/dto", "SaveDTO.java"));

            add(new GeneratorTemplateDTO("model/vo/listVO.java.vm", "model/vo", "ListVO.java"));
            add(new GeneratorTemplateDTO("model/vo/detailVO.java.vm", "model/vo", "DetailVO.java"));

            add(new GeneratorTemplateDTO("model/converter/converter.java.vm", "model/converter", "Abstract", "Converter.java", false));

            add(new GeneratorTemplateDTO("mapper.xml.vm", "", "", "Mapper.xml", true));
        }};


        List<FileOutConfig> fileOutConfigList = new ArrayList<>();
        for (GeneratorTemplateDTO dto : javaFilePathList) {
            fileOutConfigList.add(new FileOutConfig(String.format("%s/%s", templateRootPath, dto.getTemplateFile())) {
                @Override
                public String outputFile(TableInfo tableInfo) {
                    return String.format("%s/%s/%s%s%s",
                            dto.isResourceOutFile() ? resourcesRootPath : javaRootPath,
                            dto.getOutFilePath(),
                            dto.getOutFilePrefix(),
                            tableInfo.getEntityName(),
                            dto.getOutFileSuffix());
                }
            });
        }

        return fileOutConfigList;
    }

    private void checkProfile() {
        if (!qdProperties.getGeneratorEnabled()) {
            throw new BusinessException("此功能不可用");
        }
    }
}
